<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Payment
 * @subpackage Gateway
 * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id:  $
 */

/**
 * @see Zend_Payment
 */
require_once 'Tavs/Payment.php';

/**
 * @see Zend_Http_Client
 */
require_once 'Zend/Http/Client.php';

/**
 * @see Zend_Payment_Gateway_Exception
 */
require_once 'Tavs/Payment/Gateway/Exception.php';

/**
 * Class for online Ecommerce charging/payments
 *
 * More developers information and field names can be found
 * @link https://cms.paypal.com/cms_content/US/en_US/files/developer/PP_NVPAPI_DeveloperGuide.pdf
 *
 * List of all error codes can be found here
 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_errorcodes
 *
 * List of all paypal button images
 * @link http://www.atwebresults.com/paypal_buttons/
 *
 * AVS and CVV2 Response Codes
 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_AVSResponseCodes
 *
 * @category   Zend
 * @package    Zend_Payment
 * @subpackage Gateway
 * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id:  $
 */
class Tavs_Payment_Gateway_Paypal extends Tavs_Payment_Gateway_Abstract
{

	/**
	 * Required options per gateways, things such as suplier, merchant account id
	 * email addresess
	 *
	 * Other:
	 * 		 'gatewayNamespace' => custom namespace for a gateway
	 *
	 * @var array
	 */
	public $options = array(

	'api_username' => null , // api user name
'api_password' => null , // api password
'api_signature' => null , // api signature
'debug' => false)

	;

	/**
	 * Paypal Data Transfer
	 *
	 * @var bool
	 */
	protected $PDT = false;

	/**
	 * Certificate .pem file used instead of a signature
	 *
	 * @var bool
	 */
	protected $certificateFile = false;

	/**
	 * Identity token
	 *
	 * @var string
	 */
	protected $identityToken = null;

	/**
	 * Allowed card types
	 *
	 * @var array
	 */
	public $allowed_card_types = array(

	'Visa' , 'MasterCard' , 'Discover' , 'Amex' , 'Maestro' , //  Currency code must be GBP In addition, either STARTDATE or ISSUENUMBER must be specified.
'Solo')//  Currency code must be GBP In addition, either STARTDATE or ISSUENUMBER must be specified.


	;

	/**
	 * @var array | array of fields key=>value pairs
	 */
	public $fields = array(

	'PAYMENTACTION' => Tavs_Payment_Gateway_Abstract::Payment_Type_Sale , 'CURRENCYCODE' => 'USD')

	;

	/**
	 * Api version number
	 *
	 * @var float
	 */
	const API_VERSION = '52.0';

	/**
	 * Paypal website pro payments CMD possible values
	 *
	 */
	const CMD_BUYNOW = '_xclick'; // The button that the person clicked was a Buy Now button.

	const CMD_DONATE = '_donations'; // The button that the person clicked was a Donate button.

	const CMD_SUBSCRIPTION = '_xclick-subscriptions'; // The button that the person clicked was a Subscribe button.

	const CMD_GIFT = '_oe-gift-certificate'; // The button that the person clicked was a Buy Gift Certificate button.


	/**
	 * For shopping cart purchases; these additional variables specify the kind of shopping cart button that the person clicked:
	 * add – Add to Cart buttons for the PayPal Shopping Cart
	 * display – View Cart buttons for the PayPal Shopping Cart
	 * upload – The Cart Upload command for third party carts
	 *
	 */
	const CMD_CART = '_cart';

	const CMD_SBUYNOW = '_s-xclick'; // The button that the person clicked was protected from tampering by using encryption.

	const CMD_PDT_DETAILS = '_notify-synch'; // Used in PDT

	const CMD_NOTIFY = '_notifyvalidate'; // Used in IPN


	/**
	 * Paypal secured URL
	 *
	 */
	const PAYPAL_URL = 'https://www.paypal.com/cgi-bin/webscr';

	const PAYPAL_SANDBOX_URL = 'https://www.sandbox.paypal.com/cgi-bin/webscr';

	const LIVE_URI_CERT = 'https://api.paypal.com/nvp';

	const LIVE_URI_SIG = 'https://api-3t.paypal.com/nvp';

	const TEST_URI_CERT = 'https://api.sandbox.paypal.com/nvp';

	const TEST_URI_SIG = 'https://api-3t.sandbox.paypal.com/nvp';

	/**
	 * Raw body content
	 *
	 * @var string
	 */
	protected $_rawResponseBody;

	/**
	 * Constructor.
	 *
	 * $options is an array of key/value pairs or an instance of Zend_Config
	 * containing configuration options.
	 *
	 * @param  array|Zend_Config $options An array or instance of Zend_Config having configuration data
	 * @param object Zend_Http_Client
	 * @throws Tavs_Payment_Exception
	 */
	public function __construct($options, Zend_Http_Client $client = null)
	{
		parent::__construct($options, $client);

		$this->client->setUri($this->makeUri());

		$this->client->setMethod(Zend_Http_Client::POST);

	}

	/**
	 * Check for config options that are mandatory.
	 *
	 * @throws Tavs_Payment_Gateway_Exception
	 */
	public function checkRequiredOptions()
	{
		print_r($this->options);
		if ((bool) $this->PTD !== true) {
			$required = array('api_username' , 'api_password' , 'api_signature');
			foreach ($required as $required_field) {
				if (! $this->options[$required_field]) {
					$this->throwException("{$required_field} must be specifed", Tavs_Payment_Gateway_Abstract::MISSING_REQ_OPTIONS);
				}
			}
		}

		if ((bool) $this->PTD !== true) {
			# Set required values
			$this->addParam('USER', $this->options['api_username']);
			$this->addParam('PWD', $this->options['api_password']);
			$this->addParam('SIGNATURE', $this->options['api_signature']);
			$this->addParam('VERSION', self::API_VERSION);
		}
	}

	/**
	 * GetExpressCheckoutDetails API Call
	 *
	 * Obtain information about an Express Checkout transaction.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_GetExpressCheckoutDetails
	 *
	 */
	public function getExpressCheckout($_token)
	{
		$this->addParam('TOKEN', urlencode($_token));
		$this->addParam('METHOD', 'GetExpressCheckoutDetails');

		return $this->_buildRequest();
	}

	/**
	 * DoExpressCheckoutPayment API Call
	 *
	 * Completes an Express Checkout transaction.
	 * If you set up a billing agreement in your SetExpressCheckout API call,
	 * the billing agreement is created when you call the DoExpressCheckoutPayment API operation.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_DoExpressCheckoutPayment
	 *
	 * @param string $_token
	 * @param string $_payerid
	 * @param float $_amount
	 * @return array
	 */
	public function doExpressCheckout($_token, $_payerid, $_amount)
	{
		$this->addParam('TOKEN', urlencode($_token));
		$this->addParam('PAYERID', urlencode($_payerid));
		$this->addParam('METHOD', 'DoExpressCheckoutPayment');
		$this->addParam('AMT', $_amount);

		$this->_buildRequest();

		# In case we will have an IPN
		return $this->parseIPNResponse();
	}

	/**
	 * SetCustomerBillingAgreement API Call
	 *
	 * Initiates the creation of a billing agreement.
	 * Note:
	 * If you are using Express Checkout with version 54.0 or later of the API,
	 * do not use the SetCustomerBillingAgreement API operation to set up a billing agreement.
	 * Use the SetExpressCheckout API operation instead.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_SetCustomerBillingAgreement
	 *
	 * @param string $returnURL
	 * @param string $cancelURL
	 * @param float $amount
	 * @param string $billDesc
	 * @param string $paymentType
	 * @param boolean $autoContinueOnSuccess
	 * @return unknown
	 */
	public function setCustomerBillingAgreement($returnURL, $cancelURL, $amount, $billDesc, $paymentType = 'Any', $autoContinueOnSuccess = false)
	{
		$this->addParam('METHOD', 'SetCustomerBillingAgreement');
		$this->addParam('AMT', $amount);
		$this->addParam('ReturnURL', $returnURL);
		$this->addParam('CancelURL', $cancelURL);

		$this->addParam('BILLINGTYPE', urlencode('RecurringPayments'));
		$this->addParam('BILLINGAGREEMENTDESCRIPTION', urlencode($billDesc));
		$this->addParam('PAYMENTTYPE', urlencode($paymentType));

		# auto continue?
		if ($autoContinueOnSuccess === true) {
			$response = $this->_buildRequest();
			if ($this->isSuccess()) {
				$this->continueBillingAgreement($response['TOKEN']);
			} else {
				return $response;
			}
		} else {
			return $this->_buildRequest();
		}
	}

	/**
	 * DoDirectPayment API call
	 *
	 * Process a credit card payment.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_DoDirectPayment
	 *
	 * @param array $creditInfo
	 * @param array $billingInfo
	 * @param float $amount
	 * @return array
	 */
	public function doDirectPayment(array $creditInfo = array(), array $billingInfo = array(), $amount)
	{
		$this->addParam('METHOD', 'DoDirectPayment');
		$this->addParam('AMT', $amount);
		$this->addParam('IPADDRESS', $_SERVER['REMOTE_ADDR']);

		# Credit Info
		if (count($creditInfo)) {
			foreach ($creditInfo as $_creditInfo_key => $_creditInfo_value) {
				$this->addParam($_creditInfo_key, $_creditInfo_value);
			}
		}

		# Billing Address Info
		if (count($billingInfo)) {
			foreach ($billingInfo as $_billingInfo_key => $_billingInfo_value) {
				$this->addParam($_billingInfo_key, $_billingInfo_value);
			}
		}

		$this->_buildRequest();

		# In case we will have an IPN
		return $this->parseIPNResponse();

	}

	/**
	 * DoReferenceTransaction API Call
	 *
	 * Process a payment from a buyer’s account, which is identified by a previous transaction.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_DoReferenceTransaction
	 *
	 * @param string $ReferenceId
	 * @param array $creditInfo
	 * @param array $billingInfo
	 * @param float $amount
	 * @return array
	 */
	public function doReferenceTransaction($ReferenceId, array $creditInfo = array(), array $billingInfo = array(), $amount)
	{
		$this->addParam('METHOD', 'DoReferenceTransaction');
		$this->addParam('AMT', $amount);
		$this->addParam('REFERENCEID', urlencode($ReferenceId));

		# Credit Info
		if (count($creditInfo)) {
			foreach ($creditInfo as $_creditInfo_key => $_creditInfo_value) {
				$this->addParam($_creditInfo_key, $_creditInfo_value);
			}
		}

		# Billing Address Info
		if (count($billingInfo)) {
			foreach ($billingInfo as $_billingInfo_key => $_billingInfo_value) {
				$this->addParam($_billingInfo_key, $_billingInfo_value);
			}
		}

		$this->_buildRequest();

		# In case we will have an IPN
		return $this->parseIPNResponse();
	}

	/**
	 * ManagePendingTransactionStatus API Call
	 *
	 * Accept or deny a pending transaction held by Fraud Management Filters.
	 *
	 * Accept - accepts the payment
	 * Deny - rejects the payment
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_ManagePendingTransactionStatus
	 *
	 * @param string $transactionID
	 * @param string $actionType - should be Accept/Deny
	 * @return array
	 */
	public function managePendingTransactionStatus($transactionID, $actionType = 'Accept')
	{
		$this->addParam('METHOD', 'ManagePendingTransactionStatus');
		$this->addParam('TRANSACTIONID', urlencode($transactionID));
		$this->addParam('ACTION', $actionType);

		return $this->_buildRequest();
	}

	/**
	 * DoCapture API Call
	 *
	 * Capture an authorized payment.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_DoCapture
	 *
	 * @param string $authorizationID
	 * @param float $amount
	 * @param string $captureType
	 * @return array
	 */
	public function doCapture($authorizationID, $amount, $captureType = 'Complete')
	{
		$this->addParam('METHOD', 'DoCapture');
		$this->addParam('AMT', urlencode($amount));
		$this->addParam('AUTHORIZATIONID', urlencode($authorizationID));
		$this->addParam('COMPLETETYPE', urlencode($captureType));

		return $this->_buildRequest();
	}

	/**
	 * CreateRecurringPaymentsProfile API Call
	 *
	 * Create a recurring payments profile.
	 * You must invoke the CreateRecurringPaymentsProfile API operation for each profile you want to create.
	 * The API operation creates a profile and an associated billing agreement.
	 *
	 * Note: There is a one-to-one correspondence between billing agreements and recurring payments profiles.
	 * To associate a a recurring payments profile with its billing agreement,
	 * the description in the recurring payments profile must match the description of a billing agreement.
	 * For version 54.0 and later, use SetExpressCheckout to initiate creation of a billing agreement.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_CreateRecurringPayments
	 *
	 * @param string $token
	 * @param array $creditInfo
	 * @param array $billingInfo
	 * @param string $startDate
	 * @param string $billPeriod = Day/Week/SemiMonth/Month/Year
	 * @param string $billFreq
	 * @param float $amount
	 * @param string $desc
	 * @return array
	 */
	public function createRecurringPayment($token, array $creditInfo = array(), array $billingInfo = array(), $startDate, $billPeriod, $billFreq, $amount, $desc)
	{
		$this->addParam('METHOD', 'CreateRecurringPaymentsProfile');
		$this->addParam('TOKEN', urlencode($token));

		$this->addParam('PROFILESTARTDATE', $startDate);
		$this->addParam('DESC', urlencode($desc));
		$this->addParam('AMT', urlencode($amount));

		$this->addParam('BILLINGPERIOD', $billPeriod);
		$this->addParam('BILLINGFREQUENCY', $billFreq);

		# Credit Info
		if (count($creditInfo)) {
			foreach ($creditInfo as $_creditInfo_key => $_creditInfo_value) {
				$this->addParam($_creditInfo_key, $_creditInfo_value);
			}
		}

		# Billing Address Info
		if (count($billingInfo)) {
			foreach ($billingInfo as $_billingInfo_key => $_billingInfo_value) {
				$this->addParam($_billingInfo_key, $_billingInfo_value);
			}
		}

		return $this->_buildRequest();
	}

	/**
	 * UpdateRecurringPaymentsProfile API Call
	 *
	 * Update a recurring payments profile.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_UpdateRecurringPaymentsProfile
	 *
	 * @param string $profileID
	 * @param array $creditInfo
	 * @param array $billingInfo
	 * @param string $startDate
	 * @param string $billPeriod = Day/Week/SemiMonth/Month/Year
	 * @param string $billFreq
	 * @param float $amount
	 * @param string $desc
	 * @return array
	 */
	public function updateRecurringPayment($profileID, array $creditInfo = array(), array $billingInfo = array(), $startDate, $billPeriod, $billFreq, $amount, $desc)
	{
		$this->addParam('METHOD', 'UpdateRecurringPaymentsProfile');
		$this->addParam('PROFILEID', urlencode($profileID));

		$this->addParam('PROFILESTARTDATE', $startDate);
		$this->addParam('DESC', urlencode($desc));
		$this->addParam('AMT', urlencode($amount));

		$this->addParam('BILLINGPERIOD', $billPeriod);
		$this->addParam('BILLINGFREQUENCY', $billFreq);

		# Credit Info
		if (count($creditInfo)) {
			foreach ($creditInfo as $_creditInfo_key => $_creditInfo_value) {
				$this->addParam($_creditInfo_key, $_creditInfo_value);
			}
		}

		# Billing Address Info
		if (count($billingInfo)) {
			foreach ($billingInfo as $_billingInfo_key => $_billingInfo_value) {
				$this->addParam($_billingInfo_key, $_billingInfo_value);
			}
		}

		return $this->_buildRequest();
	}

	/**
	 * MassPay API Call
	 *
	 * Make a payment to one or more PayPal account holders.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_MassPay
	 *
	 * @param array $_receivers
	 * @return array/null
	 */
	public function massPay(array $_receivers = array())
	{
		if (count($_receivers) == 0) {
			$this->throwException("There were no email addresses set", Tavs_Payment_Gateway_Abstract::MISSING_REQ_OPTIONS);
		}

		$this->addParam('METHOD', 'MassPay');
		foreach ($_receivers as $i => $receiver) {
			$this->addParam('L_EMAIL' . $i, $receiver['email']);
			$this->addParam('L_AMT' . $i, urlencode($receiver['amount']));
			$this->addParam('L_UNIQUEID' . $i, urlencode($receiver['uniqueID']));
			$this->addParam('L_NOTE' . $i, urlencode($receiver['note']));
		}

		return $this->_buildRequest();
	}

	/**
	 * Callback API Call
	 *
	 * Updates the PayPal Review page with shipping options, insurance, and tax information.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_CallbackAPI
	 *
	 * @return array
	 */
	public function callBack()
	{
		$this->addParam('METHOD', 'Callback');

		return $this->_buildRequest();
	}

	/**
	 * BillOutstandingAmount API Call
	 *
	 * Bill the buyer for the outstanding balance associated with a recurring payments profile.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_BillOutstandingAmount
	 *
	 * @param string $profileID
	 * @return array
	 */
	public function billOutstandingAmount($profileID)
	{
		$this->addParam('METHOD', 'BillOutstandingAmount');
		$this->addParam('PROFILEID', urlencode($profileID));

		return $this->_buildRequest();
	}

	/**
	 * AddressVerify API Call
	 *
	 * Confirms whether a postal address and postal code match those of the specified PayPal account holder.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_AddressVerify
	 *
	 * @param string $emailAddress
	 * @param string $streetAddress
	 * @param int $zipCode
	 * @param bool return all array
	 * @return bool|array
	 */
	public function isVerifiedAddress($emailAddress, $streetAddress, $zipCode, $returnFullArray = false)
	{
		$this->addParam('METHOD', 'AddressVerify');
		$this->addParam('EMAIL', $emailAddress);
		$this->addParam('STREET', urlencode($streetAddress));
		$this->addParam('ZIP', $zipCode);

		if ($returnFullArray) {
			return $this->_buildRequest();
		}

		$this->_buildRequest();
		return $this->isSuccess();

	}

	/**
	 * GetRecurringPaymentsProfileDetails API Call
	 *
	 * Obtain information about a recurring payments profile.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_GetRecurringPaymentsProfileDetails
	 *
	 * @param string $profileId
	 * @return array
	 */
	public function getRecurringPaymentDetails($profileId)
	{
		$this->addParam('METHOD', 'GetRecurringPaymentsProfileDetails');
		$this->addParam('PROFILEID', $profileId);

		return $this->_buildRequest();
	}

	/**
	 * DoReauthorization API Call
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_DoReauthorization
	 *
	 * @param string $authorizationID
	 * @param float $amount
	 * @return array
	 */
	public function doReauthorization($authorizationID, $amount)
	{
		$this->addParam('METHOD', 'DoReauthorization');
		$this->addParam('AMT', urlencode($amount));
		$this->addParam('AUTHORIZATIONID', urlencode($authorizationID));

		return $this->_buildRequest();
	}

	/**
	 * DoVoid API Call
	 *
	 * Void an order or an authorization.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_DoVoid
	 *
	 * @param string $authorizationID
	 * @return array
	 */
	public function doVoid($authorizationID)
	{
		$this->addParam('METHOD', 'DoVoid');
		$this->addParam('AUTHORIZATIONID', urlencode($authorizationID));

		return $this->_buildRequest();
	}

	/**
	 * GetBalance API Call
	 *
	 * Obtain the available balance for a PayPal account.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_GetBalance
	 *
	 * @param string $authorizationID
	 * @return unknown
	 */
	public function getBalance()
	{
		$this->addParam('METHOD', 'GetBalance');

		return $this->_buildRequest();
	}

	/**
	 * RefundTransaction API Call
	 *
	 * Issue a refund to the PayPal account holder associated with a transaction.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_RefundTransaction
	 *
	 * @param string $authorizationID
	 * @param string $type Full/Partial/Other
	 * @param float $amount only when type set is Partial
	 * @return array
	 */
	public function refundTransaction($authorizationID, $type = 'Full', $amount = null)
	{
		$this->addParam('METHOD', 'RefundTransaction');
		$this->addParam('AMT', urlencode($amount));
		$this->addParam('TRANSACTIONID', urlencode($authorizationID));
		$this->addParam('REFUNDTYPE', urlencode($type));

		return $this->_buildRequest();

	}

	/**
	 * TransactionSearch API Call
	 *
	 * Search transaction history for transactions that meet the specified criteria.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_TransactionSearch
	 *
	 * @param string $_startdate
	 * @return array
	 */
	public function transactionSearch($_startdate)
	{
		$this->addParam('METHOD', 'TransactionSearch');
		$this->addParam('STARTDATE', $_startdate);

		return $this->_buildRequest();
	}

	/**
	 * Did we get any results from out last transaction search?
	 *
	 * @return boolean
	 */
	public function hasSearchResults()
	{
		return $this->_response['L_TRANSACTIONID0'] ? true : false;
	}

	/**
	 * Return an array of results from the transaction search
	 *
	 * @return array
	 */
	public function getSearchResults()
	{
		if (! $this->hasSearchResults()) {
			return;
		}

		$count = 0;
		$results = array();
		while (isset($this->_response['L_TRANSACTIONID' . $count])) {
			$results[] = array(

			'timestamp' => $this->_response['L_TIMESTAMP' . $count] , // L_TIMESTAMP
'timezone' => $this->_response['L_TIMEZONE' . $count] , // L_TIMEZONE
'type' => $this->_response['L_TYPE' . $count] , // L_TYPE
'name' => $this->_response['L_NAME' . $count] , // L_NAME
'transactionid' => $this->_response['L_TRANSACTIONID' . $count] , // L_TRANSACTIONID
'status' => $this->_response['L_STATUS' . $count] , // L_STATUS
'amt' => $this->_response['L_AMT' . $count] , // L_AMT
'currencycode' => $this->_response['L_CURRENCYCODE' . $count] , // L_CURRENCYCODE
'fee' => $this->_response['L_FEEAMT' . $count] , // L_FEEAMT
'net' => $this->_response['L_NETAMT' . $count])// L_NETAMT


			;
			$count ++;
		}

		return $results;
	}

	/**
	 * Total transaction search results
	 *
	 * @return int
	 */
	public function totalSearchResults()
	{
		$count = 0;
		while (isset($this->_response['L_TRANSACTIONID' . $count])) {
			$count ++;
		}

		return $count;
	}

	/**
	 * DoAuthorization API Call
	 *
	 * Authorize a payment.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_DoAuthorization
	 *
	 * @param string $_transactionID
	 * @param float $amount
	 * @return array
	 */
	public function doAuthorization($authorizationID, $amount)
	{
		$this->addParam('METHOD', 'DoAuthorization');
		$this->addParam('AMT', urlencode($amount));
		$this->addParam('TRANSACTIONID', urlencode($authorizationID));
		$this->addParam('TRXTYPE', urlencode('V'));

		return $this->_buildRequest();

	}

	/**
	 * Generate a button Paypal website pro url, For donation purposes, buy now buttons
	 * Subscriptions and more.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_html_Appx_websitestandard_htmlvariables
	 *
	 * @param string $_cmd
	 * @param string $email
	 * @param float $amount
	 * @param boolean $autoRedirect
	 * @return string
	 */
	public function buildButton($_cmd, $email, $amount, $autoRedirect = false)
	{
		$_stack = array();
		$_stack[] = "cmd={$_cmd}";
		$_stack[] = "business={$email}";
		$_stack[] = "amount={$amount}";
		if (count($this->fields)) {
			foreach ($this->fields as $key => $value) {
				$_stack[] = "{$key}={$value}";
			}
		}

		# Build everything
		if ($this->options['debug']) {
			$_urlCombined = self::PAYPAL_SANDBOX_URL . '?' . implode("&", $_stack);
		} else {
			$_urlCombined = self::PAYPAL_URL . '?' . implode("&", $_stack);
		}

		if ($autoRedirect === true) {
			header("Location: " . $_urlCombined);
			return;
		}

		return $_urlCombined;
	}

	/**
	 * GetTransactionDetailsPDT API Call
	 *
	 * PayPal returns related variables for each kind of IPN or PDT message. Not all variables are returned for each type of transaction.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_html_IPNandPDTVariables
	 *
	 * @param string $transactionID
	 * @param string $identityToken
	 * @return array
	 */
	public function getTransactionDetailsPDT($transactionID, $identityToken = null)
	{
		# Token specified?
		if ($identityToken == null && $this->options['identityToken'] == null) {
			$this->throwException('You must specify an identity token', Tavs_Payment_Gateway_Abstract::FILL_ALL_FIELDS);
		}

		if ($identityToken) {
			$this->options['identityToken'] = $identityToken;
		}

		$this->addParam('at', $this->options['identityToken']);
		$this->addParam('tx', $transactionID);
		$this->addParam('cmd', self::CMD_PDT_DETAILS);

		$this->client->setUri($this->options['debug'] ? self::PAYPAL_SANDBOX_URL : self::PAYPAL_URL);

		$this->_buildRequest();

		return $this->_parsePDTResponse();
	}

	/**
	 * Parse the IPN body data
	 *
	 * Available at 'NOTIFYURL' field of the DoDirectPayment, DoExpressCheckoutPayment, or DoReferenceTransaction
	 *
	 * PayPal returns related variables for each kind of IPN or PDT message. Not all variables are returned for each type of transaction.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_html_IPNandPDTVariables
	 *
	 * @return array
	 */
	public function parseIPN($transactionID)
	{
		$this->addParam('cmd', self::CMD_NOTIFY);
		$this->addParam('txn_id', $transactionID);

		$this->client->setUri($this->options['debug'] ? self::PAYPAL_SANDBOX_URL : self::PAYPAL_URL);

		$this->_buildRequest();

		$this->_rawResponseBody = explode("\n", $this->_rawResponseBody);

		if ($this->_rawResponseBody[0] == "VERIFIED") {
			array_shift($this->_rawResponseBody);

			array_pop($this->_rawResponseBody);

			foreach ($this->_rawResponseBody as $theValue) {
				$xxx = explode("=", $theValue);

				$this->_response[urldecode($xxx[0])] = urldecode($xxx[1]);
			}

			$this->_response['IPNVERIFED'] = 'OK';
		} else {
			$this->_response['IPNERRORMESSAGE'] = $this->_rawResponseBody[0];
			$this->_response['IPNVERIFED'] = 'Failed';
		}

		return $this->_response;
	}

	/**
	 * GetTransactionDetails API Call
	 *
	 * Obtain information about a specific transaction.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_GetTransactionDetails
	 *
	 * @param string $authorizationID
	 * @return array
	 */
	public function getTransactionDetails($authorizationID)
	{
		$this->addParam('METHOD', 'GetTransactionDetails');
		$this->addParam('TRANSACTIONID', urlencode($authorizationID));

		return $this->_buildRequest();
	}

	/**
	 * Operation failed
	 *
	 * @param array $params
	 * @return boolean
	 */
	public function isFailed(array $params = array())
	{
		return $this->isSuccess($params) ? false : true;
	}

	/**
	 * Do we have any warnings
	 *
	 */
	public function hasWarnings()
	{
		return ($this->_response['ACK'] == 'SuccessWithWarning' || $this->_response['ACK'] == 'FailureWithWarning') ? true : false;
	}

	/**
	 * Get the error codes returned
	 *
	 */
	public function getErrorCodes()
	{
		$count = 0;
		$codes = array();
		while (isset($this->_response['L_SHORTMESSAGE' . $count])) {
			$codes[] = $this->_response['L_ERRORCODE' . $count];
			$count ++;
		}

		return $codes;
	}

	/**
	 * Get the error messages returned
	 *
	 */
	public function getErrorMessages()
	{
		$count = 0;
		$messages = array();
		while (isset($this->_response['L_SHORTMESSAGE' . $count])) {
			$messages[] = $this->_response['L_LONGMESSAGE' . $count];
			$count ++;
		}

		return $messages;
	}

	/**
	 * Was the Operation successful
	 *
	 * @param array $params
	 * @return boolean
	 */
	public function isSuccess(array $params = array())
	{
		if (count($params)) {
			return ($params['ACK'] == 'Success' || $params['ACK'] == 'SuccessWithWarning') ? true : false;
		}

		return ($this->_response['ACK'] == 'Success' || $this->_response['ACK'] == 'SuccessWithWarning') ? true : false;
	}

	/**
	 * SetExpressCheckout API Call
	 *
	 * Initiates an Express Checkout transaction.
	 * Optionally, the SetExpressCheckout API operation can set up billing agreements for reference transactions and recurring payments.
	 *
	 * @link https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_SetExpressCheckout
	 *
	 * @param float $_amount
	 * @param string $_returnurl
	 * @param string $_cancelurl
	 * @param boolean $autoContinueOnSuccess
	 * @return array
	 */
	public function setExpressCheckout($_amount, $_returnurl, $_cancelurl, $autoContinueOnSuccess = false)
	{
		$this->addParam('METHOD', 'SetExpressCheckout');

		$this->addParam('AMT', $_amount);
		$this->addParam('ReturnURL', $_returnurl);
		$this->addParam('CancelURL', $_cancelurl);

		# auto continue?
		if ($autoContinueOnSuccess === true) {
			$response = $this->_buildRequest();
			if ($this->isSuccess()) {
				$this->continueExpressCheckout($response['TOKEN']);
			} else {
				return $response;
			}
		} else {
			return $this->_buildRequest();
		}
	}

	/**
	 * Continue Express checkout
	 *
	 * @param string $token
	 */
	public function continueExpressCheckout($token)
	{
		$_RedirectUrl = "https://www.paypal.com/webscr&cmd=_express-checkout&token={$token}";
		if ($this->options['debug']) {
			$_RedirectUrl = "https://www.sandbox.paypal.com/webscr&cmd=_express-checkout&token={$token}";
		}
		header("Location: $_RedirectUrl");
		return;
	}

	/**
	 * continueBillingAgreement
	 *
	 * @param string $token
	 */
	public function continueBillingAgreement($token)
	{
		$_RedirectUrl = "https://www.paypal.com/webscr&cmd=_customer-billing-agreement&token={$token}";
		if ($this->options['debug']) {
			$_RedirectUrl = "https://www.sandbox.paypal.com/webscr&cmd=_customer-billing-agreement&token={$token}";
		}
		header("Location: $_RedirectUrl");
		return;
	}

	/**
	 * Prepare the credit card information
	 *
	 * 0 -> credit card type
	 * 1 -> credit card number
	 * 2 -> expiration date. month format MM
	 * 3 -> expiration date. year format YYYY
	 * 4 -> cvv number
	 * 5 -> payers first name
	 * 6 -> middle name
	 * 7 -> last name
	 * 8 -> Month and year that Maestro or Solo card was issued, the MMYYYY format.
	 * 9 -> Issue number of Maestro or Solo card.Character length: two numeric digits maximum.
	 *
	 * @param array $values
	 */
	public function setCreditInfo($_cardType, $_cardNumber, $_epxMonth, $_expYear, $_CVV, $_firstName, $_middleName = null, $_lastName, $_solostartdate = null, $_soloinvnumber = null)
	{

		# Card type
		if (! in_array(ucwords($_cardType), $this->allowed_card_types)) {
			$this->throwException("The card type {$_cardType} is not supported by this gateway.", Tavs_Payment_Gateway_Abstract::CREDIT_TYPE_NOT_SUPPORTED);
		}

		# Credit Card
		if (! Zend_Validate::is($_cardNumber, 'Ccnum')) {
			$this->throwException("Credit card number supplied is invalid.", Tavs_Payment_Gateway_Abstract::CCNUMBER_INVALID);
		}

		return array(

		'CREDITCARDTYPE' => $_cardType , // credit card type
'ACCT' => $_cardNumber , // credit card number
'EXPDATE' => str_pad($_epxMonth, 2, '0', STR_PAD_LEFT) . $_expYear , // expiration date. format MMYYYY
'CVV2' => $_CVV , // cvv number
'STARTDATE' => $_solostartdate , // Month and year that Maestro or Solo card was issued, the MMYYYY format.
'ISSUENUMBER' => $_soloinvnumber , // Issue number of Maestro or Solo card.Character length: two numeric digits maximum.
'FIRSTNAME' => $_firstName , // payers first name
'MIDDLENAME' => $_middleName , // middle name
'LASTNAME' => $_lastName)// last name


		;
	}

	/**
	 * Prepare the billing information
	 *
	 * 0 -> street address
	 * 1 -> city
	 * 2 -> country code
	 * 3 -> state
	 * 4 -> zipcode
	 *
	 */
	public function setBillingInfo($_streetAddress, $_city, $_countryCode, $_state, $_zipCode)
	{
		return array(

		'STREET' => urlencode($_streetAddress) , // street address
'CITY' => urlencode($_city) , // city
'COUNTRYCODE' => urlencode($_countryCode) , // country code
'STATE' => urlencode($_state) , // state code
'ZIP' => urlencode($_zipCode))// zip code


		;
	}

	/**
	 * What kind of request to perform
	 *
	 * @param string $key
	 * @param string $value
	 * @return object
	 */
	protected function addParam($key, $value)
	{
		$this->client->setParameterPost($key, $value);
	}

	/**
	 * Determine which URL to use
	 *
	 */
	protected function makeUri()
	{
		if ($this->options['debug']) {
			return $this->certificateFile ? self::TEST_URI_CERT : self::TEST_URI_SIG;
		}

		return $this->certificateFile ? self::LIVE_URI_CERT : self::LIVE_URI_SIG;
	}

	/**
	 * Build the request and return response array
	 *
	 * @return array
	 */
	protected function _buildRequest()
	{
		# Add all other values
		if (count($this->_fields)) {
			foreach ($this->_fields as $key => $value) {
				$this->addParam($key, $value);
			}
		}

		# Send
		try {
			$response = $this->client->request();
		} catch (Zend_Http_Client_Exception $e) {
			$msg = "Message: {$e->getMessage()}, Code: {$e->getCode()}";
			$this->throwException($msg, Tavs_Payment_Gateway_Abstract::GATEWAY_INACCESSIBLE);
		}

		# No Response
		if (! $response) {
			$this->errorString = $response->getMessage();
			$this->errorCode = $response->getStatus();
		}

		# Get response
		$this->_response = $response->getBody();
		$this->_rawResponseBody = $response->getBody();

		# Explode the response
		$this->explodeResponse();

		# Empty the fields array
		$this->fields = array();

		return $this->_response;
	}

	/**
	 * Explode the returned response to an array of key=>value pairs
	 *
	 */
	protected function explodeResponse()
	{
		# Explode the response & assigne the values to the response array
		// Extract the response details.
		$httpResponseAr = explode("&", $this->_response);

		$httpParsedResponseAr = array();
		foreach ($httpResponseAr as $i => $value) {
			$tmpAr = explode("=", $value);
			if (sizeof($tmpAr) > 1) {
				$httpParsedResponseAr[$tmpAr[0]] = urldecode($tmpAr[1]);
			}
		}

		# Assigne the new response
		$this->_response = $httpParsedResponseAr;
	}

	/**
	 * For now just return the response array
	 *
	 *
	 * @todo change this to use the VERIFIED/INVALID respectivly
	 * @return array
	 */
	protected function parseIPNResponse()
	{
		return $this->_response;
	}

	/**
	 * Parse the PDT body data
	 *
	 * @return array
	 */
	protected function _parsePDTResponse()
	{
		$this->_rawResponseBody = explode("\n", $this->_rawResponseBody);

		if ($this->_rawResponseBody[0] == "SUCCESS") {
			array_shift($this->_rawResponseBody);

			array_pop($this->_rawResponseBody);

			foreach ($this->_rawResponseBody as $theValue) {
				$xxx = explode("=", $theValue);

				$this->_response[urldecode($xxx[0])] = urldecode($xxx[1]);
			}

			$this->_response['ACK'] = 'Success';
		} else {
			$this->_response['L_SHORTMESSAGE0'] = $this->_rawResponseBody[0];
			$this->_response['ACK'] = 'Failed';
		}

		return $this->_response;
	}
}